package com.yfbao.horizon.service;

import cn.hutool.core.collection.CollectionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 类加载机制
 */
public final class JarLoaderUtil {
	
	private static final Logger logger = LoggerFactory.getLogger(JarLoaderUtil.class);

    /**
     * URLClassLoader的addURL方法
     */
    private static Method addURL = initAddMethod();

    private static volatile ClassFileloader classFileloader= null;


    /**
     * 初始化方法
     */
    private static Method initAddMethod() {
        try {
            Method add = URLClassLoader.class
                    .getDeclaredMethod("addURL", new Class[]{URL.class});
            add.setAccessible(true);
            return add;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 循环遍历目录，找出所有的JAR包
     */
    public static void loopFiles(File file, List<File> files) {
        if (file.isDirectory()) {
            File[] tmps = file.listFiles();
            for (File tmp : tmps) {
                loopFiles(tmp, files);
            }
        } else {
            if (file.getAbsolutePath().endsWith(".jar")) {
                files.add(file);
            }
        }
    }

    /**
     * 循环遍历目录，找出所有的JAR包
     */
    public static void loopFilesEndWith(File file, List<File> files,String endWith) {
        if (file.isDirectory()) {
            File[] tmps = file.listFiles();
            for (File tmp : tmps) {
                loopFilesEndWith(tmp, files,endWith);
            }
        } else {
            if (file.getAbsolutePath().endsWith(endWith)) {
                files.add(file);
            }
        }
    }

    /**
     * 将指定jar 文件里面的class 加载到当前classLoader中
     *
     * @param file
     */
    public static void loadJarFile(File file) {
        loadJarFile(file, null);
    }


    /**
     * 将指定jar 文件里面的class 加载到当前classLoader中
     *
     * @param file
     */
    public static void loadJarFile(File file, ClassLoader classLoader) {
        try {
            if (classLoader == null) {
                classLoader = ClassLoader.getSystemClassLoader();
            }
            //调用系统方法，解决：class 加载和资源加载的问题
            //所以访问资源也是可以的
            logger.info(String.format("加载Jar 包：%s",file.getAbsolutePath()));
            addURL.invoke(classLoader, new Object[]{file.toURI().toURL()});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 从一个目录加载所有JAR文件
     *
     * @param path        jar 文件路径
     * @param classLoader 加载classLoader
     */
    public static void loadJarPath(String path, ClassLoader classLoader) {
        if (path == null || path.length() == 0) {
        	logger.info("jar 包加载路径为空，不加载任何jar 文件。");
            return;
        }

        List<File> files = new ArrayList<File>();
        File lib = new File(path);
        loopFiles(lib, files);
        if (CollectionUtil.isEmpty(files)) {
        	logger.info(String.format("jar 包加载路径：%s 下不存在jar 文件。未加载任何jar 包。", path));
            return;
        }

        for (File file : files) {
            loadJarFile(file, classLoader);
        }
    }

    /**
     * 加载指定目录下的jar 包：包含子目录
     *
     * @param path
     */
    public static void loadJarPath(String path) {
        loadJarPath(path, null);
    }


    public static Class<?> getClass(String className){
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

        Class<?> aClass = null;
        if(classFileloader!=null){
            try {
                aClass = classFileloader.findClass(className);
                logger.info("在classPath中找到类:"+className);
            } catch (Exception e) {
                logger.error("在classPath中找类:"+className+"异常",e);
            }
        }
        if(aClass  !=null){
            return aClass;
        }
        try {
            aClass = systemClassLoader.loadClass(className);
            logger.info("在jar中找类:"+className);
        } catch (Exception e) {
            logger.error("在jar中找到类:"+className+"异常",e);
        }
        return  aClass;
    }

    public static void loadClassFiles(String classPath){
        classFileloader = new ClassFileloader(ClassLoader.getSystemClassLoader(), classPath);
    }


//    public static void main(String[] args) {
////        String jarPath="D:\\V5\\v5-merge\\application\\ApacheJetspeed\\webapps\\seeyon\\WEB-INF\\lib\\";
//        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
////        String classPath ="D:\\V5\\v5-merge\\application\\ApacheJetspeed\\webapps\\seeyon\\WEB-INF\\classes";
////        loadClassFiles(classPath);
//        String jarPath="D:\\workspace\\testjar";
//        loadJarPath(jarPath,systemClassLoader);
//        String className="com.seeyon.ctp.common.web.filter.JSPAuthenticator";
//        Class<?> aClass = getClass(className);
//        if(aClass ==null){
//            System.out.println("未找到类："+className);
//        }
//        Collection<String> anonymouswhiteList = getFieldValue(aClass, "anonymouswhiteList");
//        logger.info("size:"+anonymouswhiteList.size());
//        for (String s : anonymouswhiteList) {
//            logger.info(s);
//        }
//    }

    public static Collection<String> getFieldValue(Class<?> clazz , String fieldName) {
        List<String> list = new ArrayList<>();
        try {

            Object obj = clazz.newInstance();
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                if (declaredField.getName().equalsIgnoreCase(fieldName)) {
                    Object o = declaredField.get(obj);
                    if (o instanceof Collection) {
                        Collection o1 = (Collection) o;
                        return o1;
                    }
                }
            }
            logger.error("无法找到calss:" +clazz.getName()+" 中的属性"+fieldName);
        } catch (Throwable e) {
            logger.error("获取jar中class:"+clazz.getName()+"的属性："+fieldName+"异常",e);
//            throw new RuntimeException("解析异常");
            return null;
        }
        logger.info(fieldName+" size:"+list.size());
        return list;
    }

    public static Set<Class<?>> getAllClassFromLoader(ClassLoader classLoader){
        if(classLoader ==null){
            classLoader = ClassLoader.getSystemClassLoader();
        }
//        File file = new File("classloderClasses.txt");
//        if (file.exists()) {
//            file.delete();
//        }
        Set<Class<?>> classesSet = new HashSet<>();
        if (classLoader != null) {
            try {
                Class clClass = classLoader.getClass();
                while (clClass != ClassLoader.class) {
                    clClass = clClass.getSuperclass();
                }
                java.lang.reflect.Field classesField = clClass.getDeclaredField("classes");
                classesField.setAccessible(true);
                Vector classes = (Vector) classesField.get(classLoader);
                for (Object aClass : classes) {
//                    Class<?> aClass1 = aClass.getClass();
//                    System.out.println(aClass.toString());
                    classesSet.add((Class) aClass);
                }
//                FileOutputStream fos = new FileOutputStream("classloderClasses.txt", true);
//                fos.write(("********************" + classLoader.toString() +" ********************").getBytes());
//                fos.write(Arrays.toString(classes.toArray()).getBytes());
//                fos.close();
            } catch (Exception exception) {
                exception.printStackTrace();
                // TODO
            }
        }
        return classesSet;
    }


    public static Set<Class<?>> loadJarAndGetClasses(String file, ClassLoader classLoader){
        if(classLoader ==null){
            classLoader = ClassLoader.getSystemClassLoader();
        }
        loadJarFile(new File(file));
        Set<Class<?>> classNames = new HashSet<>();
        try (JarFile jarFile = new JarFile(file)) {
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = entries.nextElement();
                String entryName = jarEntry.getName();
                if (entryName != null && entryName.endsWith(".class")) {
                    if(entryName.contains("BOOT-INF")){
                        int i = entryName.indexOf("BOOT-INF/classes");
                        entryName =entryName.substring(i+17);
                    }
                    entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
                    Class<?> theClass = null;
                    try {
                        theClass = classLoader.loadClass(entryName);
                    } catch (ClassNotFoundException e) {
                        System.out.println("找不到类："+entryName);
                        continue;
//                        e.printStackTrace();
                    }
                    classNames.add(theClass);
                }
                if(entryName != null && entryName.endsWith(".jar")){

                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return classNames;
    }


    public static List<Class<?>> getClassListByInterface(String url, ClassLoader classLoader,Class<?> clazz) {
        List<Class<?>> classList = new ArrayList<>();
        if (!clazz.isInterface()) {
            return classList;
        }
        List<String> classNames = new ArrayList<>();
        try (JarFile jarFile = new JarFile(url)) {
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = entries.nextElement();
                String entryName = jarEntry.getName();
                if (entryName != null && entryName.endsWith(".class")) {
                    entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
                    classNames.add(entryName);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        if (classNames.size() > 0) {
            for (String className : classNames) {
                try {
                    Class<?> theClass = classLoader.loadClass(className);
                    if (clazz.isAssignableFrom(theClass) && !theClass.equals(clazz)) {
                        classList.add(theClass);
                    }
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return classList;
    }
}
